﻿/* original TransformInteractionControl:
 * Anders Bursjöö
 * Scale, Move and rotate controls in your GUI - WPF
 * http://bursjootech.blogspot.com/2008/09/scale-move-and-rotate-controls-in-your_21.html 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Road3App.Views.UserControls
{
    public class TransformControl : ContentControl
    {
        // xmlns:customControls="clr-namespace:Road3App.Views.UserControls"

        #region Fields

        private Point _previousPosition = new Point();
        private double _previousAngle = 0;
        private int _interaction = 0; // 0 - none, 1 - move, 2 - rotate
        private Panel _controlContainer = null;
        private int _previousZIndex;

        #endregion

        #region Properties

        public static readonly DependencyProperty AngleProperty = 
            DependencyProperty.Register("Angle", typeof(double), typeof(TransformControl), new PropertyMetadata(0.0));
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }

        public static readonly DependencyProperty XProperty = 
            DependencyProperty.Register("X", typeof(double), typeof(TransformControl), new PropertyMetadata(0.0));
        public double X
        {
            get { return (double)GetValue(XProperty); }
            set { SetValue(XProperty, value); }
        }

        public static readonly DependencyProperty YProperty = 
            DependencyProperty.Register("Y", typeof(double), typeof(TransformControl), new PropertyMetadata(0.0));
        public double Y
        {
            get { return (double)GetValue(YProperty); }
            set { SetValue(YProperty, value); }
        }

        public static readonly DependencyProperty AllowMoveProperty = 
            DependencyProperty.Register("AllowMove", typeof(bool), typeof(TransformControl), new PropertyMetadata(true));
        public bool AllowMove
        {
            get { return (bool)GetValue(AllowMoveProperty); }
            set { SetValue(AllowMoveProperty, value); }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == XProperty || e.Property == YProperty)
            {
                this.Margin = new Thickness(X, Y, 0, 0);
            }

            base.OnPropertyChanged(e);
        }

        #endregion

        #region Math Methods

        private double GetAngle(Point currentPosition)
        {
            Point cc = new Point(this.ActualWidth / 2, this.ActualHeight / 2);
            //Point pp = new Point(_previousPosition.X - X, _previousPosition.Y - Y);
            //Point cp = new Point(currentPosition.X - X, currentPosition.Y - Y);
            Point pp = new Point(_previousPosition.X, _previousPosition.Y);
            Point cp = new Point(currentPosition.X, currentPosition.Y);
            double px = pp.X - cc.X;
            double py = pp.Y - cc.Y;
            double cx = cp.X - cc.X;
            double cy = cp.Y - cc.Y;

            double pa = Math.Atan2(py, px);
            double ca = Math.Atan2(cy, cx);
            double angle = (ca - pa) * (180 / Math.PI);

            return angle;
        }

        private void Rotate(double angle)
        {
            Angle = angle - angle % 15 + _previousAngle;
        }

        private void Translate(double deltaX, double deltaY)
        {
            //int gridSize = 20;

            //deltaX -= deltaX % gridSize;
            //deltaY -= deltaY % gridSize;

            Func<double, int, int> gridStep = (coordinate, gridSize) =>
                {
                    int c = (int)Math.Truncate(coordinate);
                    int mod = c % gridSize;
                    c -= mod;

                    return c;
                };

            double x = X + deltaX;
            double y = Y + deltaY;

            X = gridStep(x, 20);
            Y = gridStep(y, 20);
        }

        #endregion

        #region Methods

        public override void OnApplyTemplate()
        {
            Ellipse RotateControl = this.GetTemplateChild("Rotate") as Ellipse;
            RotateControl.MouseLeftButtonDown += RotateControlLeftButtonDown;

            Ellipse MoveControl = this.GetTemplateChild("Move") as Ellipse;
            MoveControl.MouseLeftButtonDown += MoveControlLeftButtonDown;

            _controlContainer = this.GetTemplateChild("ControlGrid") as Panel;

            base.OnApplyTemplate();
        }

        private void MoveControlLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.CaptureMouse();
            _interaction = 1;
            _previousPosition = e.GetPosition(this);
        }

        private void RotateControlLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.CaptureMouse();
            _interaction = 2;
            _previousPosition = e.GetPosition(this);
            _previousAngle = Angle;
        }

        void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _interaction = 0;
            //Mouse.Capture(null);
            this.ReleaseMouseCapture();
        }

        void Control_MouseMove(object sender, MouseEventArgs e)
        {
            if (_interaction != 0)
            {
                Point currentPosition = e.GetPosition(this);

                if (_interaction == 1) // move
                {
                    Translate(currentPosition.X - _previousPosition.X, currentPosition.Y - _previousPosition.Y);
                }
                else if (_interaction == 2) // rotate
                {
                    double angle = GetAngle(currentPosition);
                    Rotate(angle);
                }

                //_previousPosition = currentPosition;
            }
        }

        void Control_MouseLeave(object sender, MouseEventArgs e)
        {
            _controlContainer.Visibility = System.Windows.Visibility.Hidden;
            Canvas.SetZIndex(this, _previousZIndex);
        }

        void Control_MouseEnter(object sender, MouseEventArgs e)
        {
            if (AllowMove)
            {
                _controlContainer.Visibility = System.Windows.Visibility.Visible;
                _previousZIndex = Canvas.GetZIndex(this);
                Canvas.SetZIndex(this, 999999);
            }
        }

        #endregion

        #region Constructors

        static TransformControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TransformControl), new FrameworkPropertyMetadata(typeof(TransformControl)));
        }

        public TransformControl()
        {
            this.MouseEnter += Control_MouseEnter;
            this.MouseLeave += Control_MouseLeave;
            this.MouseMove += Control_MouseMove;
            this.MouseLeftButtonUp += Control_MouseLeftButtonUp;
        }

        #endregion
    }
}
